use syn::{GenericArgument, Ident, PathArguments};
use quote::quote;
use syn::{ItemFn, ReturnType, Type};
use crate::utils;

pub fn generate_return_code(func: &ItemFn, struct_name: &Ident, params_order: &[Ident]) -> proc_macro2::TokenStream {
    let aqua = utils::get_aqua_path();
    let handle_name: syn::Ident = func.sig.ident.clone();
    let is_async = func.sig.asyncness.is_some();

    let call_expression: proc_macro2::TokenStream = if is_async {
        quote! { #struct_name :: #handle_name(#(#params_order),*).await }
    } else {
        quote! { #struct_name :: #handle_name(#(#params_order),*) }
    };

    match extract_return_type(func) {
        HandleReturnType::None => {
            quote! {
                #call_expression;
                #aqua::http::HttpResponse::ok()
            }
        }
        HandleReturnType::Type(ty) => {
            quote! {
                let _: fn() -> () = || {
                    fn assert_impl<T: #aqua::convert::IntoResponse>() {}
                    assert_impl::<#ty>();
                };
                let result = #call_expression;
                result.into_response()
            }
        }
        HandleReturnType::Option(ty) => {
            quote! {
                let _: fn() -> () = || {
                    fn assert_impl<T: #aqua::convert::IntoResponse>() {}
                    assert_impl::<#ty>();
                };
                match #call_expression {
                    None => #aqua::http::HttpResponse::ok(),
                    Some(result) => result.into_response(),
                }
            }
        }
        HandleReturnType::Result(ok_ty, err_ty) => {
            quote! {
                let _: fn() -> () = || {
                    fn assert_ok<T: #aqua::convert::IntoResponse>() {}
                    fn assert_err<E: #aqua::convert::IntoResponse>() {}
                    assert_ok::<#ok_ty>();
                    assert_err::<#err_ty>();
                };
                match #call_expression {
                    Ok(result) => result.into_response(),
                    Err(result) => result.into_response(),
                }
            }
        }
        HandleReturnType::ResultUnitOk(err_ty) => {
            quote! {
                let _: fn() -> () = || {
                    fn assert_err<E: #aqua::convert::IntoResponse>() {}
                    assert_err::<#err_ty>();
                };
                match #call_expression {
                    Ok(()) => #aqua::http::HttpResponse::ok(),
                    Err(result) => result.into_response(),
                }
            }
        }
    } 
}

#[allow(unused)]
enum HandleReturnType {
    None,
    Type(Type),                 // i32、String、MyStruct
    Option(Type),               // Option<T>
    Result(Type, Type),         // Result<T, E>
    ResultUnitOk(Type),         // Result<(), E>
}

fn extract_return_type(func: &ItemFn) -> HandleReturnType {
    match &func.sig.output {
        ReturnType::Default => HandleReturnType::None,
        ReturnType::Type(_, ty) => match &**ty {
            Type::Path(type_path) => {
                if let Some(seg) = type_path.path.segments.last() {
                    let ident = &seg.ident;
                    if ident == "Result" {
                        if let PathArguments::AngleBracketed(args) = &seg.arguments {
                            let args: Vec<_> = args.args.iter().collect();
                            if args.len() == 2 {
                                if let (GenericArgument::Type(ok_ty), GenericArgument::Type(err_ty)) = (&args[0], &args[1]) {
                                    // 检查 ok 是否为 ()
                                    if is_unit_type(ok_ty) {
                                        return HandleReturnType::ResultUnitOk(err_ty.clone());
                                    } else {
                                        return HandleReturnType::Result(ok_ty.clone(), err_ty.clone());
                                    }
                                }
                            }
                        }
                    } else if ident == "Option" {
                        if let PathArguments::AngleBracketed(args) = &seg.arguments {
                            if let Some(GenericArgument::Type(inner_ty)) = args.args.first() {
                                return HandleReturnType::Option(inner_ty.clone());
                            }
                        }
                    }
                }
                HandleReturnType::Type((**ty).clone())
            }
            _ => HandleReturnType::Type((**ty).clone())
        }
    }
}

fn is_unit_type(ty: &Type) -> bool {
    matches!(ty, Type::Tuple(tuple) if tuple.elems.is_empty())
}
